/*

	Funciona de forma identica ao Eratostenes.cpp
	Veja os comentarios la.

	A diferneca e' que, o omp parallel for
	divide o conjunto de indices percorridos entre
	as threads,
	assim, digamos que hajam os indices a serem percorridos
	{3, 6, 9, 12, 15, 18, ...} e duas threads, o OMP
	podera fazer uma thread percorrer os valores
	{3, 9, 15, ...},
	enquanto a outrao percorrera os valores
	{6, 12, 18, ...}.
*/


#include <EratostenesOpenMP.hpp>
#include <omp.h>

EratostenesOpenMP::EratostenesOpenMP(unsigned n)
: Eratostenes(n) {

}

void EratostenesOpenMP::calculate(long long *icount, unsigned nThreads) {
	if (calculated) return;
	calculated = true;

	long long *myicount = new long long [nThreads];

	sieve[0] = false;

	int id = omp_get_thread_num();

	#pragma omp parallel for
	for (int i = 1; i <= sieveSz; i++) {
		sieve[i] = true;
		myicount[id] += 3L;
	}

	// Nao e' utilizado omp parallel for aqui
	// devido a interdependencia dos valores
	// (pode resultar em valores incorretos)
	for (int i = 3; i * i <= n; i += 2) {
		if (sieve[i>>1]) {
			int k = i << 1;

			#pragma omp parallel for
			for (int j = i*i; j <= n; j += k) {
				sieve[j>>1] = false;
				myicount[id] += 3L;
			}
		}
	}
	if(icount)
		for (int i = 0; i < nThreads; i++)
			icount[i] += myicount[i];

	delete [] myicount;
}
